package com.geotmt.billingcenter.modules.designmode.rulesfactory.impl;

import com.alibaba.druid.util.StringUtils;
import com.geotmt.billingcenter.common.entry.StairRule;
import com.geotmt.billingcenter.common.enumpackage.ConsumeFeeFlagEnum;
import com.geotmt.billingcenter.common.enumpackage.CostFeeFlagEnum;
import com.geotmt.billingcenter.common.enumpackage.RuleLabelEnum;
import com.geotmt.billingcenter.common.enumpackage.StairCycleEnum;
import com.geotmt.billingcenter.common.exception.MyException;
import com.geotmt.billingcenter.common.utils.GeoDateUtils;
import com.geotmt.billingcenter.common.utils.SpringUtil;
import com.geotmt.billingcenter.common.vo.consume.ConsumeStatisVo;
import com.geotmt.billingcenter.common.vo.cost.CostStatisVo;
import com.geotmt.billingcenter.common.vo.params.ParamsConsume;
import com.geotmt.billingcenter.common.vo.params.ParamsCost;
import com.geotmt.billingcenter.common.vo.params.RuleVo;
import com.geotmt.billingcenter.modules.designmode.rulesfactory.RulesFactory;
import com.geotmt.billingcenter.modules.rules.service.RulesService;
import com.geotmt.billingcenter.modules.statis.service.ConsumeStatisService;
import com.geotmt.billingcenter.modules.statis.service.CostStatisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @Description 阶梯计费
 *              阶梯计费周期      1:年 2：月 3：日
 *              阶梯计费开始时间： 日期
 * @Author      yanghanwei
 * @Mail        yanghanwei@geotmt.com
 * @Date        2019/3/25 13:25
 * @Version     v1
 **/
@Component
public class StairBilling implements RulesFactory {

    private static final Logger logger = LoggerFactory.getLogger(StairBilling.class);

    private RulesService rulesService;

    private ConsumeStatisService consumeStatisService;

    private CostStatisService costStatisService;

    public StairBilling(){
        rulesService = SpringUtil.getBean(RulesService.class);
        consumeStatisService = SpringUtil.getBean(ConsumeStatisService.class);
        costStatisService = SpringUtil.getBean(CostStatisService.class);
    }

    @Override
    public Double getMoney(RuleVo ruleVo) throws Exception{
        logger.info("阶梯计费 ==== 具体实现");
        String startDateStr = "startDate";
        String endDateStr = "endDate";
        String conName = "consume";
        String costName = "cost";
        Double money;
        if(null == ruleVo || StringUtils.isEmpty(ruleVo.getType()) || StringUtils.isEmpty(ruleVo.getSign())){
            throw new MyException("type或sign不能为null", HttpServletResponse.SC_NOT_ACCEPTABLE);
        }
        List<StairRule> stairList = new ArrayList<>();
        List<StairRule> stairRuleList = ruleVo.getStairRuleList();
        if(stairRuleList.size() > 0){
            stairList = stairRuleList;
        } else {
            stairList = rulesService.stairListGetByRid(ruleVo.getId(), getLabel(ruleVo));
        }
        int totalHis = 0;
        //获取阶梯历史计费周期
        Map<String, Date> dateMap = this.geAreDate(ruleVo);
        if(dateMap.containsKey(startDateStr) && dateMap.containsKey(endDateStr)){
            Date startDate = dateMap.get(startDateStr);
            Date endDate = dateMap.get(endDateStr);
            if(startDate.getTime() < endDate.getTime()){
                //计算阶梯周期内历史数量
                if(conName.equals(ruleVo.getType())){
                    //消耗
                    totalHis = getHisCountForConsume(ruleVo, startDate, endDate);
                }
                if(costName.equals(ruleVo.getType())){
                    //成本
                    totalHis = getHisCountForCost(ruleVo, startDate, endDate);
                }
            }
        }
        //根据阶梯计费规则计算金额
        money = this.getMoney(stairList, totalHis, getCount(ruleVo));
        return money;
    }


    /**
     * 获取消耗历史数量
     * @param ruleVo
     * @param startDate
     * @param endDate
     * @return
     * @throws Exception
     */
    private Integer getHisCountForConsume(RuleVo ruleVo, Date startDate, Date endDate) throws Exception{
        int totalHis = 0;
        ParamsConsume param = new ParamsConsume();
        BeanUtils.copyProperties(ruleVo,param);
        param.setEffectStartTime(startDate);
        param.setEffectEndTime(endDate);
        ConsumeStatisVo consumeStatisVo = consumeStatisService.hisConsumeStatis(param);
        if(null != consumeStatisVo ){
            Integer countCharging = consumeStatisVo.getCountCharging();
            if(null != countCharging){
                totalHis = countCharging;
            }
            logger.info("消耗周期内的历史量：{}",totalHis);
        }else{
            logger.info("没有查到记录，请检查");
        }
        return totalHis;
    }

    /**
     * 获取成本历史数量
     * @param ruleVo
     * @param startDate
     * @param endDate
     * @return
     * @throws Exception
     */
    private Integer getHisCountForCost(RuleVo ruleVo, Date startDate, Date endDate) throws Exception{
        int totalHis = 0;
        ParamsCost param = new ParamsCost();
        BeanUtils.copyProperties(ruleVo,param);
        param.setEffectStartTime(startDate);
        param.setEffectEndTime(endDate);
        CostStatisVo costStatisVo = costStatisService.hisCostStatis(param);
        if(null != costStatisVo){
            Integer countCharging = costStatisVo.getCountCharging();
            if(null != countCharging){
                totalHis = countCharging;
            }
            logger.info("成本周期内的历史量：{}",totalHis);
        }else{
            logger.info("没有查到记录，请检查");
        }
        return totalHis;
    }

    /**
     * 获取阶梯计费的label
     * 1：consume_curr 2:consume_his 3:cost_curr 4:cost_his
     * @param ruleVo
     * @return
     */
    private Integer getLabel(RuleVo ruleVo){
        logger.info("获取阶梯计费的label,param:{}",ruleVo);
        Integer result= null;
        String conName = "consume";
        String costName = "cost";
        String curr = "curr";
        String his = "his";
        if(conName.equals(ruleVo.getType())){
            if(curr.equals(ruleVo.getSign())){
                result = RuleLabelEnum.CONSUME_CURR.value;
            }else if(his.equals(ruleVo.getSign())){
                result = RuleLabelEnum.CONSUME_HIS.value;
            }
        } else if(costName.equals(ruleVo.getType())){
            if(curr.equals(ruleVo.getSign())){
                result = RuleLabelEnum.COST_CURR.value;
            }else if(his.equals(ruleVo.getSign())){
                result = RuleLabelEnum.COST_HIS.value;
            }
        }
        logger.info("获取阶梯计费的label ：{}",result);
        return result;
    }

    /**
     * 获取要计算金额的量（查询/查得）
     * @return
     */
    private int getCount(RuleVo ruleVo){
        logger.info("获取要计算金额的量（查询/查得）,param:{}",ruleVo);
        int count = 0;
        if(null != ruleVo.getCountCharging()){
            count = ruleVo.getCountCharging();
        }
        logger.info("获取要计算金额的量（查询/查得）,result:{}",count);
        return count;
    }

    /**
     * 根据阶梯规则计算金额
     * @param stairList
     * @param countHis
     * @param count
     * @return
     */
    public Double getMoney(List<StairRule> stairList, int countHis, int count){
        logger.info("根据阶梯规则计算金额,stairList:{},countHis:{},count:{}",stairList,countHis,count);
        Double result = 0d;
        if(null != stairList && !stairList.isEmpty()){
            for(StairRule rule : stairList){
                Integer min = rule.getMin();
                Integer max = rule.getMax();
                Double price = rule.getPrice();
                if(null != max){
                    if((countHis+1) >= min && count > 0 && countHis <= max){
                        if(countHis + count <= max){
                            result += count * price;
                            count = 0;
                            countHis = max;
                        }else{
                            result += (max - countHis) * price;
                            count -= (max - countHis);
                            countHis += max - countHis;
                        }
                    }
                }else{
                    result += count * price;
                    countHis += count;
                    count = 0;
                }
                logger.info( "result:{}",result);
            }
        }
        logger.info( "根据阶梯规则计算金额 result:{}", result);
        return result;
    }


    /**
     * 获取 阶梯计费的开始结束时间
     * @param ruleVo
     * @return
     */
    private Map<String, Date> geAreDate(RuleVo ruleVo){
        Map<String,Date> repMap = new HashMap<>(2);
        Date startDate = null;
        Date endDate = null;
        Calendar calendar = Calendar.getInstance();
        if(StairCycleEnum.YEAR.value.equals(ruleVo.getStairCycle())){
            repMap = this.getAreDateYear(calendar,ruleVo,startDate,endDate);
        } else if(StairCycleEnum.QUARTER.value.equals(ruleVo.getStairCycle())){
            //季

        } else if(StairCycleEnum.MONTH.value.equals(ruleVo.getStairCycle())){
            //月(最好为月初，不能为 28,29,30,31日)
            repMap = this.getAreDateMonth(calendar,ruleVo,startDate,endDate);
        } else if(StairCycleEnum.DAY.value.equals(ruleVo.getStairCycle())){
            //日
            repMap = this.getAreDateDay();
        }
        return repMap;
    }

    /**
     * 计算年的阶梯周期
     * @param calendar
     * @param ruleVo
     * @param startDate
     * @param endDate
     * @return
     */
    private Map<String,Date> getAreDateYear(Calendar calendar, RuleVo ruleVo, Date startDate, Date endDate){
        Map<String,Date> repMap = new HashMap<>(2);
        calendar.setTime(ruleVo.getStairStartTime());
        startDate = ruleVo.getStairStartTime();
        while(true){
            calendar.add(Calendar.YEAR,1);
            calendar.add(Calendar.DATE,-1);
            //周期的结束时间
            String startDateStr = GeoDateUtils.dateStr(startDate, GeoDateUtils.DATE_FORMAT2);
            String endDateStr = GeoDateUtils.dateStr(calendar.getTime(), GeoDateUtils.DATE_FORMAT2);
            String nowDateStr = GeoDateUtils.dateStr(GeoDateUtils.getPreDay(new Date(),-1), GeoDateUtils.DATE_FORMAT2);
            if(Integer.parseInt(endDateStr) >= Integer.parseInt(nowDateStr) && Integer.parseInt(startDateStr) <= Integer.parseInt(nowDateStr)){
                endDate = GeoDateUtils.getPreDay(new Date(),-2);
                break;
            }else{
                calendar.add(Calendar.DATE,1);
                startDate = calendar.getTime();
            }
        }
        repMap.put("startDate",startDate);
        repMap.put("endDate",endDate);
        return repMap;
    }

    /**
     * 计算月的阶梯周期
     * @param calendar
     * @param ruleVo
     * @param startDate
     * @param endDate
     * @return
     */
    private Map<String,Date> getAreDateMonth(Calendar calendar, RuleVo ruleVo, Date startDate, Date endDate){
        Map<String,Date> repMap = new HashMap<>(2);
        calendar.setTime(ruleVo.getStairStartTime());
        startDate = ruleVo.getStairStartTime();
        while(true){
            calendar.add(Calendar.MONTH,1);
            calendar.add(Calendar.DATE,-1);
            //周期的结束时间
            String startDateStr = GeoDateUtils.dateStr(startDate, GeoDateUtils.DATE_FORMAT2);
            String endDateStr = GeoDateUtils.dateStr(calendar.getTime(), GeoDateUtils.DATE_FORMAT2);
            String nowDateStr = GeoDateUtils.dateStr(GeoDateUtils.getPreDay(new Date(),-1), GeoDateUtils.DATE_FORMAT2);
            if(Integer.parseInt(endDateStr) >= Integer.parseInt(nowDateStr) && Integer.parseInt(startDateStr) <= Integer.parseInt(nowDateStr)){
                endDate = GeoDateUtils.getPreDay(new Date(),-2);
                break;
            }else{
                calendar.add(Calendar.DATE,1);
                startDate = calendar.getTime();
            }
        }
        repMap.put("startDate",startDate);
        repMap.put("endDate",endDate);
        return repMap;
    }

    /**
     * 计算天的阶梯周期
     * @return
     */
    private Map<String,Date> getAreDateDay(){
        Map<String,Date> repMap = new HashMap<>(2);
        repMap.put("startDate",null);
        repMap.put("endDate",null);
        return repMap;
    }


}
